/**                                                                                                                   
 *PHP V8 Library                                                                                                      
 *Copyright(C) Ins<Instance@9.cn> www.narrow-world.com
 */
extern "C"{
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
}
#include "php_phpv8.h"

#include "phpv8_context.h"
#include "phpv8_helper.h"
#include "phpv8_jsobject.h"
using namespace v8;

#define PHPV8_VERSION "0.01"
#define PHPV8_CONTEXT_DESCRIPTOR_RES_NAME "PHPV8 Context Descriptor"

/* If you declare any globals in php_phpv8.h uncomment this:
ZEND_DECLARE_MODULE_GLOBALS(phpv8)
*/

/* True global resources - no need for thread safety here */
static int le_phpv8, phpv8_context_descriptor;

/* {{{ phpv8_functions[]
 *
 * Every user visible function must have an entry in phpv8_functions[].
 */
const zend_function_entry phpv8_functions[] = {
  PHP_FE(phpv8_eval, NULL)
  PHP_FE(phpv8_create_context, NULL)
  PHP_FE(phpv8_context_eval, NULL)
  PHP_FE(phpv8_destroy_context, NULL)
  {NULL, NULL, NULL}	/* Must be the last line in phpv8_functions[] */
};
/* }}} */

/* {{{ phpv8_module_entry
 */
zend_module_entry phpv8_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
  STANDARD_MODULE_HEADER,
#endif
  "phpv8",
  phpv8_functions,
  PHP_MINIT(phpv8),
  PHP_MSHUTDOWN(phpv8),
  PHP_RINIT(phpv8),		/* Replace with NULL if there's nothing to do at request start */
  PHP_RSHUTDOWN(phpv8),	/* Replace with NULL if there's nothing to do at request end */
  PHP_MINFO(phpv8),
#if ZEND_MODULE_API_NO >= 20010901
	PHPV8_VERSION, /* Replace with version number for your extension */
#endif
	STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_PHPV8
BEGIN_EXTERN_C()
ZEND_GET_MODULE(phpv8)
END_EXTERN_C()
#endif

/* {{{ PHP_INI
 */
/* Remove comments and fill if you need to have entries in php.ini
PHP_INI_BEGIN()
    STD_PHP_INI_ENTRY("phpv8.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_phpv8_globals, phpv8_globals)
    STD_PHP_INI_ENTRY("phpv8.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_phpv8_globals, phpv8_globals)
PHP_INI_END()
*/
/* }}} */

/* {{{ php_phpv8_init_globals
 */
/* Uncomment this function if you have INI entries
static void php_phpv8_init_globals(zend_phpv8_globals *phpv8_globals)
{
	phpv8_globals->global_value = 0;
	phpv8_globals->global_string = NULL;
}
  */
/* }}} */



static void phpv8_context_descriptor_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC){
  php_printf("destroy context\n");
  PHPV8Context *context = (PHPV8Context *)(rsrc->ptr);
  if (context !=NULL)
    delete context;
}
/* {{{ PHP_MINIT_FUNCTION
 */
  PHP_MINIT_FUNCTION(phpv8)
  {
    /* If you have INI entries, uncomment these lines 
       REGISTER_INI_ENTRIES();
    */

    phpv8_context_descriptor = zend_register_list_destructors_ex(phpv8_context_descriptor_dtor, NULL, PHPV8_CONTEXT_DESCRIPTOR_RES_NAME, module_number);

    return SUCCESS;
  }
/* }}} */

/* {{{ PHP_MSHUTDOWN_FUNCTION
 */
PHP_MSHUTDOWN_FUNCTION(phpv8)
{
  /* uncomment this line if you have INI entries
     UNREGISTER_INI_ENTRIES();
  */
  return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request start */
/* {{{ PHP_RINIT_FUNCTION
 */
PHP_RINIT_FUNCTION(phpv8)
{
  return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request end */
/* {{{ PHP_RSHUTDOWN_FUNCTION
 */
PHP_RSHUTDOWN_FUNCTION(phpv8)
{
	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MINFO_FUNCTION
 */
PHP_MINFO_FUNCTION(phpv8)
{
  php_info_print_table_start();
  php_info_print_table_header(2, "phpv8 support", "enabled");
  php_info_print_table_end();

  /* Remove comments if you have entries in php.ini
     DISPLAY_INI_ENTRIES();
  */
}
/* }}} */


PHP_FUNCTION(phpv8_eval){
  char *code = NULL;
  int code_len = 0;
  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &code, &code_len) == FAILURE)
    RETURN_NULL();
  HandleScope handle_scope;
  TryCatch trycatch;
  Persistent<Context> context = Context::New();
  Context::Scope scope(context);
  Local<String> source = String::New(code, code_len);
  Local<Script> script = Script::Compile(source);
  if (script.IsEmpty()){
    if (!trycatch.Message().IsEmpty()){
      String::AsciiValue str(trycatch.Exception());
      phpv8_error(E_WARNING, "[%d]%s", trycatch.Message()->GetLineNumber(), *str);
    } else {
      phpv8_error(E_WARNING, "Unknown Error");
    }
    RETURN_NULL();
  }
  Handle<Value> result = script->Run();
  if (result.IsEmpty()){
    if (!trycatch.Message().IsEmpty()){
      String::AsciiValue str(trycatch.Exception());
      phpv8_error(E_WARNING, "[%d]%s", trycatch.Message()->GetLineNumber(), *str);
    } else {
      phpv8_error(E_WARNING, "Unknown Error");
    }
    RETURN_NULL();
  }
  String::AsciiValue str(result);
  printf("%s\n", *str);
  context.Dispose();
  RETURN_NULL();
}


PHP_FUNCTION(phpv8_create_context){
  PHPV8Context *context = new PHPV8Context();
  ZEND_REGISTER_RESOURCE(return_value, context, phpv8_context_descriptor);
}



PHP_FUNCTION(phpv8_context_eval){
  PHPV8Context *context = NULL;
  zval *context_resource;
  char *code = NULL;
  int code_len = 0, rsrc_type;
  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &context_resource, &code, &code_len) == FAILURE){
    RETURN_NULL();
  }
  context = (PHPV8Context *)zend_list_find(Z_RESVAL_P(context_resource), &rsrc_type);
  if (!context || rsrc_type != phpv8_context_descriptor){
    phpv8_error(E_WARNING, "Invalid context provided");
    RETURN_NULL();
  }
  HandleScope handle_scope;
  TryCatch trycatch;
  Context::Scope scope(context->getContext());
  Handle<String> source = String::New(code, code_len);
  Handle<Script> script = Script::Compile(source);
  if (script.IsEmpty()){
    if (!trycatch.Message().IsEmpty()){
      String::AsciiValue str(trycatch.Exception());
      phpv8_error(E_WARNING, "[%d]%s", trycatch.Message()->GetLineNumber(), *str);
    } else {
      phpv8_error(E_WARNING, "Unknown Error");
    }
    RETURN_NULL();
  }
  Handle<Value> result = script->Run(); 
  if (result.IsEmpty()){
    if (!trycatch.Message().IsEmpty()){
      String::AsciiValue str(trycatch.Exception());
      phpv8_error(E_WARNING, "[%d]%s", trycatch.Message()->GetLineNumber(), *str);
    } else {
      phpv8_error(E_WARNING, "Unknown Error");
    }    
    RETURN_NULL();
  }
  phpv8_cast_jsobject_to_zval(*result, &return_value);
}

PHP_FUNCTION(phpv8_destroy_context){
  PHPV8Context *context = NULL;
  zval *context_resource;
  int rsrc_type;
  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &context_resource) == FAILURE){
    RETURN_NULL();
  }
  context = (PHPV8Context *)zend_list_find(Z_RESVAL_P(context_resource), &rsrc_type);
  if (!context || rsrc_type != phpv8_context_descriptor){
    phpv8_error(E_WARNING, "Invalid context provided");
    RETURN_NULL();
  }
  zend_hash_index_del(&EG(regular_list), Z_RESVAL_P(context_resource));
  RETURN_TRUE;
}
